package net.wicp.tams.common.es.bean;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;

import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import net.wicp.tams.common.apiext.DateUtil;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.callback.IConvertObj;
import net.wicp.tams.common.constant.dbType.BinlogType;
import net.wicp.tams.common.es.MappingEnumSerializer;

@Builder
@Getter
public class MappingBean {
	private Map<String, Propertie> properties;
	// Whether or not new properties should be added dynamically to an existing
	// object. Accepts true (default), false and strict.
	private Dynamic dynamic;
	// 是否启用搜索
	private Boolean enabled;

	/***
	 * 
	 * @param canIndex 字段为jsonstr要不要解析为对象
	 * @param jsonstr  索引数据
	 * @return
	 */
	public static MappingBean proMappingBean(Boolean canIndex, String jsonstr) {
		MappingBeanBuilder builder = MappingBean.builder().dynamic(Dynamic.STRICT)
				.enabled(canIndex == null ? true : canIndex);
		JSONObject parseObject = JSON.parseObject(jsonstr);
		Map<String, Propertie> properties = new HashMap<>();
		for (String key : parseObject.keySet()) {
			Propertie propertieBuilder = new Propertie();
			DataTypes dataType = null;
			try {
				JSONObject jsonObject = parseObject.getJSONObject(key);
				dataType = DataTypes.getDataTypeByName(jsonObject);
				if (jsonObject.containsKey("relations")) {
					propertieBuilder.setRelations(jsonObject.getJSONObject("relations"));
				}
			} catch (Exception e) {
				dataType = DataTypes.getDataTypeByName(parseObject.getString(key));
			}
			if (dataType == null) {
				throw new RuntimeException("错误的mapping类型，请检查key:[" + key + "]的类型");
			}
			propertieBuilder.setType(dataType);
			// propertieBuilder.setEnabled(enabled);//TODO 需要扩展为 json才能用
			properties.put(key, propertieBuilder);
		}
		builder.properties(properties);
		return builder.build();
	}

	public static MappingBean proMappingBean(String jsonstr) {
		return proMappingBean(true, jsonstr);
	}

	// 判断 表是否为根，默认为是
	public static boolean isRoot(JSONObject relations, String eleName) {
		if (relations == null || relations.size() == 0) {
			return true;
		} else if (!relations.containsKey(eleName)) {
			return false;
		} else {
			List<String> values = new ArrayList<>();
			for (Object value : relations.values()) {
				try {
					String tempv = (String) value;
					values.add(tempv.split(":")[0]);
				} catch (Exception e) {
					JSONArray ary = (JSONArray) value;
					for (Object object : ary) {
						values.add(String.valueOf(object).split(":")[0]);
					}
				}
			}
			if (values.contains(eleName)) {
				return false;
			} else {
				return true;
			}
		}
	}

	// 得到rela里配置的关联名称
	public static String getRelaName(JSONObject relations, String tableName) {
		for (Object value : relations.values()) {
			try {
				String tempv = (String) value;
				if (tempv.startsWith(tableName + ":")) {
					return tempv;
				}
			} catch (Exception e) {
				JSONArray ary = (JSONArray) value;
				for (Object object : ary) {
					if (String.valueOf(object).startsWith(tableName + ":")) {
						return String.valueOf(object);
					}
				}
			}
		}
		return null;
	}

	@Data
	public static class Propertie {
		private Map<String, Propertie> properties;// object类型，它与type两个有一个不为空
		private DataTypes type;
		// Whether the JSON value given for the object field should be parsed and
		// indexed (true, default) or completely ignored (false).
		// 是否解析json为object
		private Boolean enabled;

		private JSONObject relations;// 定义类型为json时，里面会有"type"域就是DataTypes。

		/***
		 * 对超过 ignore_above 的字符串，analyzer 不会进行处理；所以就不会索引起来。导致的结果就是最终搜索引擎搜索不到了。这个选项主要对
		 * not_analyzed 字段有用，这些字段通常用来进行过滤、聚合和排序。而且这些字段都是结构化的，所以一般不会允许在这些字段中索引过长的项。
		 */
		private String ignore_above;
	}

	public enum Dynamic {
		// 默认为true
		TRUE("true"), FALSE("false"), STRICT("strict");

		private final String value;

		private Dynamic(String value) {
			this.value = value;
		}

		public String getValue() {
			return value;
		}
	}

	@SuppressWarnings("rawtypes")
	public enum DataTypes {
		TEXT("text", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		KEYWORD("keyword", new BinlogType[] { BinlogType.VARCHAR, BinlogType.TIME, BinlogType.YEAR, BinlogType.TIME2,
				BinlogType.STRING }, new IConvertObj<String>() {
					@Override
					public String getObj(String value) {
						return value;
					}
				}),

		DATE("date", new BinlogType[] { BinlogType.TIMESTAMP, BinlogType.DATE, BinlogType.DATETIME, BinlogType.NEWDATE,
				BinlogType.TIMESTAMP2, BinlogType.DATETIME2 }, new IConvertObj<Date>() {
					@Override
					public Date getObj(String value) {
						Date objToDate = DateUtil.objToDate(value);
						return objToDate;
					}
				}),

		LONG("long", new BinlogType[] { BinlogType.TINY, BinlogType.SHORT, BinlogType.LONG, BinlogType.LONGLONG,
				BinlogType.INT24, BinlogType.BIT, BinlogType.ENUM, BinlogType.SET }, new IConvertObj<Long>() {
					@Override
					public Long getObj(String value) {
						Long retobj = StringUtil.isNull(value) ? null : Long.parseLong(value);
						return retobj;
					}
				}),

		DOUBLE("double",
				new BinlogType[] { BinlogType.FLOAT, BinlogType.DOUBLE, BinlogType.DECIMAL, BinlogType.NEWDECIMAL },
				new IConvertObj<Double>() {
					@Override
					public Double getObj(String value) {
						Double retobj = StringUtil.isNull(value) ? null : Double.parseDouble(value);
						return retobj;
					}
				}),

		BOOLEAN("boolean", new BinlogType[] {}, new IConvertObj<Boolean>() {
			@Override
			public Boolean getObj(String value) {
				return StringUtil.isNull(value) ? null : Boolean.parseBoolean(value);
			}
		}),

		IP("ip", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		OBJECT("object", new BinlogType[] { BinlogType.JSON }, new IConvertObj<Object>() {
			@Override
			public Object getObj(String value) {
				throw new RuntimeException("暂不支持");
			}
		}),

		NESTED("nested", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		GEO_POINT("geo_point", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		GEO_SHAPE("geo_shape", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		// 设置join类型值
		JOIN("join", new BinlogType[] {}, new IConvertObj<String>() {
			/***
			 * 父："my_join_field": "question" 子："my_join_field": { "name": "answer",
			 * "parent": "1" }
			 */
			@Override
			public String getObj(String value) {
				return value;
			}
		}),

		COMPLETION("completion", new BinlogType[] {}, new IConvertObj<String>() {
			@Override
			public String getObj(String value) {
				return value;
			}
		});

		private final String value;

		private final IConvertObj convertObj;

		private final BinlogType[] BinlogTypes;

		public BinlogType[] getBinlogTypes() {
			return BinlogTypes;
		}

		private DataTypes(String value, BinlogType[] BinlogTypes, IConvertObj convertObj) {
			this.value = value;
			this.BinlogTypes = BinlogTypes;
			this.convertObj = convertObj;
		}

		public String getValue() {
			return value;
		}

		public Object getConvertObj(String value) {
			return this.convertObj.getObj(value);
		}

		public static DataTypes getDataTypeByName(String name) {
			if (StringUtil.isNull(name)) {
				return null;
			}
			for (DataTypes dataTypes : DataTypes.values()) {
				if (dataTypes.getValue().equalsIgnoreCase(name)) {
					return dataTypes;
				}
			}
			return null;
		}

		public static DataTypes getDataTypeByName(JSONObject filedObj) {
			String type = filedObj.getString("type");
			filedObj.remove("type");
			return getDataTypeByName(type);
		}

		public static DataTypes getDataTypesByMysqlType(BinlogType mysqlType) {
			if (mysqlType == null) {
				return null;
			}
			for (DataTypes dataTypes : DataTypes.values()) {
				if (ArrayUtils.contains(dataTypes.getBinlogTypes(), mysqlType)) {
					return dataTypes;
				}
			}
			return null;
		}
	}

	/*
	 * 6.X已不支持mapping的index只能设置为true或false public static enum Index { analyzed,
	 * not_analyzed, no; }
	 */

	@Override
	public String toString() {
		SerializeConfig tamsSerializeConfig = SerializeConfig.getGlobalInstance();
		tamsSerializeConfig.put(DataTypes.class, MappingEnumSerializer.instance);
		tamsSerializeConfig.put(Dynamic.class, MappingEnumSerializer.instance);
		return JSONObject.toJSONString(this, tamsSerializeConfig);
	}
}
